package com.grandtech.insurance.common.utils.trans;

import com.vividsolutions.jts.geom.*;
import com.vividsolutions.jts.geom.impl.CoordinateArraySequence;
import org.osgeo.proj4j.*;
import org.osgeo.proj4j.io.Proj4FileReader;

import java.util.HashMap;
import java.util.Map;

/**
 * Created by zy on 2018/7/16.
 */

public final class Pro4jUtil {


    private final static String _wgs84_param = "+title=long/lat:WGS84 +proj=longlat +ellps=WGS84 +datum=WGS84 +units=degress";

    /**
     * 中国区分带
     */
    public final static Map<Integer, String> WKID = new SimpleMap<Integer, String>()
            .push(75, "4513").push(78, "4514").push(81, "4515").push(84, "4516")
            .push(87, "4517").push(90, "4518").push(93, "4519").push(96, "4520")
            .push(99, "4521").push(102, "4522").push(105, "4523").push(108, "4524")
            .push(111, "4525").push(114, "4526").push(117, "4527").push(120, "4528")
            .push(123, "4529").push(126, "4530").push(129, "4531").push(132, "4532")
            .push(135, "4533");


    public final static Map<String, String[]> paramMap = new HashMap<>();

    /**
     * 快速计算面积（提升效率）
     *
     * @param latLngs
     * @return
     */
    static CRSFactory _crsFactory = new CRSFactory();
    static CoordinateReferenceSystem _wgs84 = _crsFactory.createFromParameters("WGS84", _wgs84_param);
    static Proj4FileReader _proj4FileReader = new Proj4FileReader();
    static CoordinateTransformFactory _ctf = new CoordinateTransformFactory();


    /**
     * 计算投影带
     *
     * @param lon
     * @return
     */
    public static String getsId(Double lon) {
        lon += 1.5;//加上1.5度，换算3度带带号
        int blt = (int) Math.floor(lon / 3);//3度带号
        int midLon = blt * 3;//中央经线
        return WKID.get(midLon);
    }

    /**
     * 84点投影到2000上
     *
     * @param latLng
     * @return
     */
    static ProjCoordinate proCoordinate84;
    static ProjCoordinate proCoordinate20000;
    static CoordinateReferenceSystem target;
    static CoordinateTransform transform;
    static String[] paramPoj;


    public synchronized static double[] quickProject84To2000(double longitude, double latitude, String sID) {
        try {
            if (paramMap.get(sID) == null) {
                paramPoj = _proj4FileReader.readParametersFromFile("epsg", sID);
                paramMap.put(sID, paramPoj);
            } else {
                paramPoj = paramMap.get(sID);
            }
            target = _crsFactory.createFromParameters(sID, paramPoj);
            transform = _ctf.createTransform(_wgs84, target);
            if (transform != null) {
                proCoordinate84 = new ProjCoordinate(longitude, latitude);
                proCoordinate20000 = new ProjCoordinate();
                transform.transform(proCoordinate84, proCoordinate20000);
                return new double[]{proCoordinate20000.x, proCoordinate20000.y};
            }
            return null;
        } catch (Exception e) {
            return null;
        }
    }

    public static String get84JtsGeometrySId(Geometry geometry) {
        if (geometry == null) return null;
        Point centerPoint = geometry.getCentroid();
        double lon = centerPoint.getX();
        return Pro4jUtil.getsId(lon);
    }

    public static Geometry proJtsGeometry84To2000(Geometry geometry) {
        String type = geometry.getGeometryType();
        if (type == null) return null;
        if (type.equals("Polygon")) {
            return proJtsPolygon84To2000(geometry);
        }
        if (type.equals("MultiPolygon")) {
            return proJtsMultiPolygon84To2000(geometry);
        }
        return null;
    }

    public static Geometry proJtsPolygon84To2000(Geometry geometry) {
        if (geometry == null) return null;
        String sId = get84JtsGeometrySId(geometry);
        if (sId == null) return null;
        GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), Integer.parseInt(sId));
        Polygon _84polygon = (Polygon) geometry;
        int innerRingCount = _84polygon.getNumInteriorRing();
        LineString _84outerLine = _84polygon.getExteriorRing();
        Coordinate[] coordinates = new Coordinate[_84outerLine.getNumPoints()];
        double[] point;
        Coordinate coordinate;
        for (int i = 0; i < _84outerLine.getNumPoints(); i++) {
            coordinate = _84outerLine.getCoordinateN(i);
            point = Pro4jUtil.quickProject84To2000(coordinate.x, coordinate.y, sId);
            coordinates[i] = new Coordinate(point[0], point[1]);
        }
        LinearRing _2000outRing = new LinearRing(new CoordinateArraySequence(coordinates), geometryFactory);
        LinearRing[] _2000innerRings = new LinearRing[innerRingCount];
        LineString _84innerLine;
        for (int i = 0; i < innerRingCount; i++) {
            _84innerLine = _84polygon.getInteriorRingN(i);
            Coordinate[] tempCoordinates = new Coordinate[_84innerLine.getNumPoints()];
            for (int j = 0; j < _84innerLine.getNumPoints(); j++) {
                coordinate = _84innerLine.getCoordinateN(j);
                point = Pro4jUtil.quickProject84To2000(coordinate.x, coordinate.y, sId);
                tempCoordinates[j] = new Coordinate(point[0], point[1]);
            }
            _2000innerRings[i] = new LinearRing(new CoordinateArraySequence(tempCoordinates), geometryFactory);
        }
        return new Polygon(_2000outRing, _2000innerRings, geometryFactory);
    }

    public static Geometry proJtsMultiPolygon84To2000(Geometry geometry) {
        if (geometry == null) return null;
        String sId = get84JtsGeometrySId(geometry);
        if (sId == null) return null;
        GeometryFactory geometryFactory = new GeometryFactory(new PrecisionModel(), Integer.parseInt(sId));
        MultiPolygon _84multiPolygon = (MultiPolygon) geometry;
        int geometryCount = _84multiPolygon.getNumGeometries();
        Geometry geometryItem;
        Geometry _2000Geometry;
        Polygon[] _2000Polygon = new Polygon[geometryCount];
        for (int i = 0; i < geometryCount; i++) {
            geometryItem = _84multiPolygon.getGeometryN(i);
            _2000Geometry = proJtsPolygon84To2000(geometryItem);
            _2000Polygon[i] = (Polygon) _2000Geometry;
        }
        MultiPolygon _2000MultiPolygon = new MultiPolygon(_2000Polygon, geometryFactory);
        return _2000MultiPolygon;
    }
}
